package cn.sciento.starter.keyencrypt.json;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.google.common.base.Objects;
import java.io.IOException;
import java.util.Arrays;
import cn.sciento.starter.keyencrypt.core.Encrypt;
import cn.sciento.starter.keyencrypt.core.EncryptContext;
import cn.sciento.starter.keyencrypt.core.EncryptType;
import cn.sciento.starter.keyencrypt.core.IEncryptionService;

public class EncryptedJsonDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
  private final IEncryptionService service;
  
  private final JsonDeserializer<Object> baseDeserializer;
  
  private final BeanProperty property;
  
  private final Encrypt encrypt;
  
  public EncryptedJsonDeserializer(IEncryptionService service, JsonDeserializer<Object> wrapped, BeanProperty property, Encrypt encrypt) {
    this.service = service;
    this.baseDeserializer = wrapped;
    this.property = property;
    this.encrypt = encrypt;
  }
  
  public Object deserialize(JsonParser parser, DeserializationContext context) throws IOException {
    String value;
    switch (EncryptContext.encryptType()) {
      case ENCRYPT:
        value = parser.getValueAsString();
        if (Arrays.<String>stream(this.encrypt.ignoreValue()).noneMatch(item -> Objects.equal(item, value)))
          return this.service.decrypt(parser, this.baseDeserializer, context, (this.property != null) ? this.property.getType() : null, this.encrypt); 
        break;
    } 
    if (this.baseDeserializer != null)
      return this.baseDeserializer.deserialize(parser, context); 
    return context.findRootValueDeserializer(this.property.getType()).deserialize(parser, context);
  }
  
  public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
    return new EncryptedJsonDeserializer(this.service, this.baseDeserializer, property, this.encrypt);
  }
}
